home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Workbench Add-On
/
Workbench Add-On - Volume 1.iso
/
BBS-Archive
/
Comm
/
AmiTCP30b2.lha
/
src
/
devs
/
agnet
/
device.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-02-27
|
36KB
|
1,390 lines
RCS_ID_C="$Id: device.c,v 4.1 1994/02/25 14:55:44 ppessi Exp $";
/*
* device.c --- SANA-II test device, device functions
*
* Author: ppessi <Pekka.Pessi@hut.fi>
*
* Copyright (c) 1993 AmiTCP/IP Group,
* Helsinki University of Technology, Finland.
* All rights reserved.
*
* Created : Thu Jan 21 17:32:55 1993 ppessi
* Last modified: Fri Feb 25 01:17:14 1994 ppessi
*
* $Log: device.c,v $
* Revision 4.1 1994/02/25 14:55:44 ppessi
* Fixed SAS C 6.51 features
*
* Revision 2.3 1993/10/20 15:17:52 jraja
* Added correct prototype for the CheckIO().
*
* Revision 2.2 1993/10/14 00:09:43 ppessi
* Changed RCS Id format
*
* Revision 2.1 93/05/14 16:48:21 ppessi
* Release version
*
*/
#include <string.h>
#include <dos/dostags.h>
#include <dos/rdargs.h>
#include <intuition/intuition.h>
#include <rexx/storage.h>
#include <rexx/rxslib.h>
#include <clib/alib_stdio_protos.h>
#ifdef __SASC
#include <clib/exec_protos.h>
#include <pragmas/exec_sysbase_pragmas.h>
#include <clib/dos_protos.h>
#include <pragmas/dos_pragmas.h>
#include <clib/utility_protos.h>
#include <pragmas/utility_pragmas.h>
#include <clib/timer_protos.h>
#include <pragmas/timer_pragmas.h>
#endif
#ifdef __GNUC__
#include <inline/exec.h>
#include <inline/dos.h>
#include <inline/utility.h>
#include <inline/timer.h>
#endif
#include "agnet.h"
#include "agnet_protos.h"
#include "agnet_rev.h"
#include "bases.h"
/* Correct prototype for the CheckIO.
* (The one in clib/exec_protos.h has wrong return value type: BOOL (16 bits)
* instead of a pointer (32 bits)!)
*/
struct IORequest * CheckIO(struct IORequest *req);
/* Local prototypes */
static ULONG AbortReq(struct MinList *minlist, struct IOSana2Req *ios2);
static VOID CloseUnit(struct AgnetDevUnit *adu, struct IOSana2Req *ios2);
static VOID ExpungeUnit(struct AgnetDevUnit *adu);
static VOID TermIO(struct IOSana2Req *ios2);
static VOID GetSpecialStats(struct AgnetDevUnit *adu, struct IOSana2Req *ios2);
static VOID GetGlobalStats(struct AgnetDevUnit *adu, struct IOSana2Req *ios2);
static VOID GetTypeStats(struct AgnetDevUnit *adu, struct IOSana2Req *ios2);
static VOID TrackType(struct AgnetDevUnit *adu, struct IOSana2Req *ios2);
static VOID UnTrackType(struct AgnetDevUnit *adu, struct IOSana2Req *ios2);
static VOID PacketReceived(struct AgnetDevUnit *adu, ULONG length, ULONG type);
static VOID PacketSent(struct AgnetDevUnit *adu, ULONG length, ULONG type);
static VOID PacketOverrun(struct AgnetDevUnit *adu);
static VOID ReceivedGarbage(struct AgnetDevUnit *adu);
static VOID PacketDropped(struct AgnetDevUnit *adu, ULONG type);
static VOID ReceivedOrphan(struct AgnetDevUnit *adu);
static VOID ConfigInterface(struct AgnetDevUnit *adu, struct IOSana2Req *ios2);
static VOID GetStationAddress(struct AgnetDevUnit *, struct IOSana2Req *);
static VOID DeviceQuery(struct AgnetDevUnit *adu, struct IOSana2Req *ios2);
static VOID Online(struct AgnetDevUnit *adu, struct IOSana2Req *ios2);
static VOID Offline(struct AgnetDevUnit *adu, struct IOSana2Req *ios2);
static VOID OnEvent(struct AgnetDevUnit *adu, struct IOSana2Req *ios2);
static VOID DoEvent(struct AgnetDevUnit *adu, ULONG event);
static VOID WritePacket(struct AgnetDevUnit *adu, struct IOSana2Req *ios2);
static VOID ReadPacket(struct AgnetDevUnit *adu, struct IOSana2Req *ios2);
static VOID ReadOrphan(struct AgnetDevUnit *adu, struct IOSana2Req *ios2);
static VOID SendPacket(struct DelayRequest *delayed);
static VOID ReceivePacket(struct DelayRequest *delayed);
static VOID DoReceive(struct AgnetDevUnit *, struct DelayRequest *delayed );
static VOID CopyBack(struct AgnetDevUnit *, struct IOSana2Req *,
struct DelayRequest *);
/*
* Device Open vector
*
* a1 - SANA2 IO Request
* a6 - Pointer to our device base
* d0 - Unit number
* d1 - Flags
*/
ULONG ASM
DevOpen(REG(a1) struct IOSana2Req *ios2,
REG(d0) ULONG s2unit,
REG(d1) ULONG s2flags)
{
struct AgnetDevice *AgnetDev = AgnetDeviceBase;
struct AgnetDevUnit *adu;
struct TagItem *bufftag;
struct Library *UtilityBase;
struct BufferManagement *bm;
BYTE status = IOERR_UNITBUSY;
/* Make sure our open remains single-threaded.
We may Wait() when starting up. If somebody
decides to DoExpunge() before we get the
semaphore, system is probably blowing up anyways.
*/
ObtainSemaphore(&AgnetDev->ad_Lock);
/* So we won't expunge ourselves... */
AgnetDev->ad_Dev_OpenCnt++;
if (s2unit < AD_MAXUNITS) /* Legal Unit */
if (adu = InitUnit(s2unit)) /* Initialize the unit? */
if (UtilityBase = OpenLibrary("utility.library", 37L)) { /* For Tag functions */
/* Allocate a structure to store the pointers to the callback routines. */
if (bm = AllocMem(sizeof(*bm), MEMF_CLEAR|MEMF_PUBLIC)) {
/* Note: I don't complain if I can't find pointers to the callback routines.
This is because there are some programs that may need to open me, but
will never use any device commands that require the callbacks. */
if (bufftag = FindTagItem(S2_CopyToBuff, (struct TagItem *)ios2->ios2_BufferManagement)) {
bm->bm_CopyToBuffer = (SANA2_CTB) bufftag->ti_Data;
}
if (bufftag = FindTagItem(S2_CopyFromBuff, (struct TagItem *)ios2->ios2_BufferManagement)) {
bm->bm_CopyFromBuffer = (SANA2_CFB) bufftag->ti_Data;
}
AddTail((struct List *)&adu->adu_BuffMgmt, (struct Node *)bm);
/* Everything went okay. */
status = 0;
AgnetDev->ad_Dev_OpenCnt++;
AgnetDev->ad_Dev_Flags &= ~LIBF_DELEXP;
adu->adu_Unit_OpenCnt++;
/* Fix up the initial io request */
ios2->ios2_BufferManagement = (VOID *)bm;
ios2->ios2_Req.io_Error = 0;
ios2->ios2_Req.io_Message.mn_Node.ln_Type = NT_REPLYMSG;
ios2->ios2_Req.io_Unit = adu;
ios2->ios2_Req.io_Device = AgnetDev;
}
CloseLibrary(UtilityBase);
}
/* See if something went wrong. */
if (status) {
ios2->ios2_Req.io_Error = status;
ios2->ios2_Req.io_Unit = (struct Unit *) -1;
ios2->ios2_Req.io_Device = (struct Device *) -1;
}
AgnetDev->ad_Dev_OpenCnt--;
ReleaseSemaphore(&AgnetDev->ad_Lock);
return status;
}
/*
* Device Close vector.
*
* a1 - IOReq
* a6 - Device Pointer
*/
BPTR ASM
DevClose(REG(a1) struct IOSana2Req *ios2)
{
struct AgnetDevice *AgnetDev = AgnetDeviceBase;
struct AgnetDevUnit *adu;
BPTR seglist = 0L;
ObtainSemaphore(&AgnetDev->ad_Lock);
adu = (struct AgnetDevUnit *)ios2->ios2_Req.io_Unit;
CloseUnit(adu, ios2);
/* Trash the io_Device and io_Unit fields so that any attempt to use this
request will die immediatly. */
ios2->ios2_Req.io_Device = (struct Device *) -1;
ios2->ios2_Req.io_Unit = (struct Unit *) -1;
AgnetDev->ad_Dev_OpenCnt--;
/* Check to see if we've been asked to expunge. */
if (AgnetDev->ad_Dev_OpenCnt == 0 && AgnetDev->ad_Dev_Flags & LIBF_DELEXP) {
Signal(AgnetDev->ad_Task, SIGBREAKF_CTRL_F);
}
ReleaseSemaphore(&AgnetDev->ad_Lock);
return seglist;
}
/*
* Device Expunge vector
*
* a6 - Device base
*
* Note: You may NEVER EVER Wait() in expunge. Period.
* Don't even *think* about it.
*/
BPTR ASM
DevExpunge(VOID)
{
struct AgnetDevice *AgnetDev = AgnetDeviceBase;
AgnetDev->ad_Dev_Flags |= LIBF_DELEXP;
Signal(AgnetDev->ad_Task, SIGBREAKF_CTRL_F);
/* We can not expunge */
return (BPTR)0L;
}
/*
* Device Reserved vector (returns 0L)
*/
ULONG ASM
DevReserved(VOID)
{
return 0L;
}
/*
* BeginIO --- dispatch incoming requests
*
* a1 - The IO request
* a6 - The device base
*/
#define SLIP_IMMEDIATES 0L /* No QUICK IO */
VOID ASM
DevBeginIO(REG(a1) struct IOSana2Req *ios2)
{
register struct AgnetDevice *ad;
ios2->ios2_Req.io_Message.mn_Node.ln_Type = NT_MESSAGE;
if (ios2->ios2_Req.io_Command < S2_END) {
if ((ios2->ios2_Req.io_Flags & IOF_QUICK) &&
((1L << ios2->ios2_Req.io_Command) & SLIP_IMMEDIATES)) {
PerformIO(ios2);
} else {
ios2->ios2_Req.io_Flags &= ~IOF_QUICK;
/* Send this to DEVICE Message Port */
ad = (struct AgnetDevice*)ios2->ios2_Req.io_Device;
PutMsg(&ad->ad_MsgPort, (struct Message *)ios2);
}
} else {
ios2->ios2_Req.io_Error = IOERR_NOCMD;
TermIO(ios2);
}
}
/*
* The device AbortIO() entry point.
*
* A1 - The IO request to be aborted.
* A6 - The device base.
*/
ULONG ASM
DevAbortIO(REG(a1) struct IOSana2Req *ios2)
{
register struct AgnetDevUnit *adu =
(struct AgnetDevUnit *)ios2->ios2_Req.io_Unit;
register ULONG result = 0L;
LockUnit(adu);
if (ios2->ios2_Req.io_Message.mn_Node.ln_Type != NT_REPLYMSG) {
switch(ios2->ios2_Req.io_Command) {
case CMD_READ: result = AbortReq(&adu->adu_Rx,ios2);
break;
case CMD_WRITE: result = AbortReq(&adu->adu_Tx,ios2);
break;
case S2_READORPHAN: result = AbortReq(&adu->adu_RxOrph,ios2);
break;
case S2_ONEVENT: result = AbortReq(&adu->adu_Events,ios2);
break;
default: result = IOERR_NOCMD;
break;
}
}
UnlockUnit(adu);
return result;
}
/*
* This funcion is used to locate an IO request in a linked
* list and abort it if found.
*/
static ULONG
AbortReq(struct MinList *minlist, struct IOSana2Req *ios2)
{
struct Node *node, *next;
ULONG result = IOERR_NOCMD;
node = (struct Node *)minlist->mlh_Head;
while (!TAILP(node)) {
next = node->ln_Succ;
if (node == (struct Node *)ios2) {
Remove((struct Node *)ios2);
ios2->ios2_Req.io_Error = IOERR_ABORTED;
TermIO(ios2);
result = 0;
}
node = next;
}
return result;
}
/*
* Real expunge
*/
BOOL
DoExpunge(struct AgnetDevice *adb)
{
ULONG i;
/* Well, if there is somebody trying to open,
they stuck with this */
ObtainSemaphore(&adb->ad_Lock);
if (adb->ad_Device.lib_OpenCnt != 0) {
ReleaseSemaphore(&adb->ad_Lock);
return FALSE;
}
Forbid();
Remove((struct Node *)adb);
Permit();
/* Free up Units */
for (i=0; i < AD_MAXUNITS; i++) {
if (adb->ad_Units[i])
ExpungeUnit((struct AgnetDevUnit *)adb->ad_Units[i]);
adb->ad_Units[i] = NULL;
}
return TRUE;
}
/*
* InitUnit
*
* Initialize (if needed) a new agnet.device Unit
*/
struct AgnetDevUnit *
InitUnit(ULONG s2unit)
{
struct AgnetDevUnit *adu;
struct AgnetDevice *adb = AgnetDeviceBase;
/* Check to see if the Unit is already up and running. If
it is, just drop through. If not, try to start it up. */
if (adu = (struct AgnetDevUnit *)adb->ad_Units[s2unit])
return adu;
/* Allocate a new Unit structure */
adu = AllocMem(sizeof(*adu), MEMF_CLEAR|MEMF_PUBLIC);
if (!adu)
return NULL;
/* Do some initialization on the Unit structure */
#if 0
NewList(&adu->adu_Unit_MsgPort.mp_MsgList);
adu->adu_Unit_MsgPort.mp_Node.ln_Type = NT_MSGPORT;
adu->adu_Unit_MsgPort.mp_Flags = PA_IGNORE;
adu->adu_Unit_MsgPort.mp_Node.ln_Name = AGNETDEVNAME;
#endif
adu->adu_UnitNum = s2unit;
adu->adu_Device = (struct Device *) adb;
/* Try to read in our configuration file */
if (!ReadConfig(adu)) {
FreeMem(adu, sizeof(*adu));
return NULL;
}
/* Initialize our list semaphore */
InitSemaphore(&adu->adu_Lock);
/* Initialize our linked lists. */
NewList((struct List *)&adu->adu_FreeToTx);
NewList((struct List *)&adu->adu_Rx);
NewList((struct List *)&adu->adu_RxOrph);
NewList((struct List *)&adu->adu_Tx);
NewList((struct List *)&adu->adu_Events);
NewList((struct List *)&adu->adu_Track);
NewList((struct List *)&adu->adu_BuffMgmt);
/* Allocate memory buffers */
DoOnline(adu);
/* Set up the Unit structure pointer in the device base */
adb->ad_Units[s2unit] = adu;
return adu;
}
/*
* CloseUnit
*
* This function closes unit and frees resources
* allocated for each opener.
*/
static VOID
CloseUnit(struct AgnetDevUnit *adu, struct IOSana2Req *ios2)
{
register struct BufferManagement *bm = ios2->ios2_BufferManagement;
LockUnit(adu);
if (bm) {
ios2->ios2_BufferManagement = NULL;
Remove((struct Node *)bm);
FreeMem(bm, sizeof(*bm));
}
adu->adu_Unit.unit_OpenCnt--;
UnlockUnit(adu);
}
/*
* ExpungeUnit
*
* This function is called from the DoExpunge routine.
* The unit structure and all subsequent allocations are freed.
*/
static VOID
ExpungeUnit(struct AgnetDevUnit *adu)
{
struct IOSana2Req *ios2;
struct SuperS2PTStats *sstats;
int i;
struct List *io_queues[5];
/* Eliminate every queues */
LockUnit(adu);
io_queues[0] = (struct List *)&adu->adu_Rx;
io_queues[1] = (struct List *)&adu->adu_Tx;
io_queues[2] = (struct List *)&adu->adu_RxOrph;
io_queues[3] = (struct List *)&adu->adu_Events;
io_queues[4] = NULL;
for (i = 0; io_queues[i]; i++) {
while(ios2 = (struct IOSana2Req *)RemHead(io_queues[i])) {
ios2->ios2_Req.io_Error = S2ERR_OUTOFSERVICE;
ios2->ios2_WireError = S2WERR_UNIT_OFFLINE;
TermIO(ios2);
}
}
/* Tracking data was allocated by us */
while(sstats = (struct SuperS2PTStats*)
RemHead((struct List *)&adu->adu_Track)) {
FreeMem(sstats, sizeof(*sstats));
}
DoOffline(adu);
UnlockUnit(adu);
FreeMem(adu, sizeof(*adu));
}
/*
* This routine is used to dispatch an IO request either from BeginIO
* or from the Unit process.
*/
VOID
PerformIO(struct IOSana2Req *ios2)
{
struct AgnetDevUnit *adu = (struct AgnetDevUnit *)ios2->ios2_Req.io_Unit;
if (ios2->ios2_Req.io_Device ==
AgnetDeviceBase->ad_Timer.tr_node.io_Device) {
ReceivePacket(ios2);
return;
}
ios2->ios2_Req.io_Error = 0;
switch(ios2->ios2_Req.io_Command) {
case CMD_READ: ReadPacket(adu,ios2);
break;
case CMD_WRITE: WritePacket(adu,ios2);
break;
case S2_DEVICEQUERY: DeviceQuery(adu,ios2);
break;
case S2_GETSTATIONADDRESS: GetStationAddress(adu,ios2);
break;
case S2_CONFIGINTERFACE: ConfigInterface(adu,ios2);
break;
#if 0
/* Non-existing commands */
case S2_ADDSTATIONALIAS:
case S2_DELSTATIONALIAS:
#endif
case S2_ADDMULTICASTADDRESS:
case S2_DELMULTICASTADDRESS:
case S2_MULTICAST:
ios2->ios2_Req.io_Error = S2ERR_NOT_SUPPORTED;
ios2->ios2_WireError = S2WERR_GENERIC_ERROR;
TermIO(ios2);
break;
case S2_BROADCAST: WritePacket(adu,ios2);
break;
case S2_TRACKTYPE: TrackType(adu,ios2);
break;
case S2_UNTRACKTYPE: UnTrackType(adu,ios2);
break;
case S2_GETTYPESTATS: GetTypeStats(adu,ios2);
break;
case S2_GETSPECIALSTATS: GetSpecialStats(adu,ios2);
break;
case S2_GETGLOBALSTATS: GetGlobalStats(adu,ios2);
break;
case S2_ONEVENT: OnEvent(adu,ios2);
break;
case S2_READORPHAN: ReadOrphan(adu,ios2);
break;
case S2_ONLINE: Online(adu,ios2);
break;
case S2_OFFLINE: Offline(adu,ios2);
break;
default:
ios2->ios2_Req.io_Error = IOERR_NOCMD;
TermIO(ios2);
break;
}
}
/*
* This function is used to return an IO request
* back to the sender.
*/
static VOID
TermIO(struct IOSana2Req *ios2)
{
if (!(ios2->ios2_Req.io_Flags & IOF_QUICK))
ReplyMsg((struct Message *)ios2);
}
/*
* This function returns any device specific statistics that
* we may have.
*
* Currently, there is none.
*
* Ethernet may require some
*/
static VOID
GetSpecialStats(struct AgnetDevUnit *adu, struct IOSana2Req *ios2)
{
struct Sana2SpecialStatHeader *stats;
stats = (struct Sana2SpecialStatHeader *)ios2->ios2_StatData;
stats->RecordCountSupplied = 0;
TermIO(ios2);
}
/*
* This function returns the global statistics for the
* slip device.
*/
static VOID
GetGlobalStats(struct AgnetDevUnit *adu, struct IOSana2Req *ios2)
{
struct Sana2DeviceStats *stats;
if (stats = (struct Sana2DeviceStats *)ios2->ios2_StatData) {
stats->PacketsReceived = adu->adu_Stats.PacketsReceived;
stats->PacketsSent = adu->adu_Stats.PacketsSent;
stats->BadData = adu->adu_Stats.BadData;
stats->Overruns = adu->adu_Stats.Overruns;
stats->UnknownTypesReceived = adu->adu_Stats.UnknownTypesReceived;
stats->Reconfigurations = adu->adu_Stats.Reconfigurations;
stats->LastStart.tv_secs = adu->adu_Stats.LastStart.tv_secs;
stats->LastStart.tv_micro = adu->adu_Stats.LastStart.tv_secs;
} else {
ios2->ios2_Req.io_Error = S2ERR_BAD_ARGUMENT;
ios2->ios2_WireError = S2WERR_NULL_POINTER;
}
TermIO(ios2);
}
/*
* This function returns statistics for a specific
* type of packet that is being tracked.
*
* Just to be thourough, I have arbitrarily picked
* the packet type for SLIP IP packets to be 2048, the
* same as that used for Ethernet. This will at least
* allow you to track IP packets.
*/
static VOID
GetTypeStats(struct AgnetDevUnit *adu, struct IOSana2Req *ios2)
{
struct Sana2PacketTypeStats *stats;
struct SuperS2PTStats *sstats;
if (stats = (struct Sana2PacketTypeStats *)ios2->ios2_StatData) {
LockUnit(adu);
sstats = (struct SuperS2PTStats *)adu->adu_Track.mlh_Head;
/* Find asked packet type from tracking list */
while (sstats->ss_Node.mln_Succ) {
if (ios2->ios2_PacketType == sstats->ss_PType) {
stats->PacketsSent = sstats->ss_Stats.PacketsSent;
stats->PacketsReceived = sstats->ss_Stats.PacketsReceived;
stats->BytesSent = sstats->ss_Stats.BytesSent;
stats->BytesReceived = sstats->ss_Stats.BytesReceived;
stats->PacketsDropped = sstats->ss_Stats.PacketsDropped;
break;
}
sstats = (struct SuperS2PTStats *)sstats->ss_Node.mln_Succ;
}
UnlockUnit(adu);
if (!sstats->ss_Node.mln_Succ) {
ios2->ios2_Req.io_Error = S2ERR_BAD_STATE;
ios2->ios2_WireError = S2WERR_NOT_TRACKED;
}
} else {
ios2->ios2_Req.io_Error = S2ERR_BAD_ARGUMENT;
ios2->ios2_WireError = S2WERR_NULL_POINTER;
}
TermIO(ios2);
}
/*
* This function adds a packet type to the list
* of those that are being tracked.
*/
static VOID
TrackType(struct AgnetDevUnit *adu, struct IOSana2Req *ios2)
{
struct SuperS2PTStats *stats;
ULONG type = ios2->ios2_PacketType;
LockUnit(adu);
stats = (struct SuperS2PTStats *)adu->adu_Track.mlh_Head;
while (stats->ss_Node.mln_Succ) {
if (type == stats->ss_PType) {
ios2->ios2_Req.io_Error = S2ERR_BAD_STATE;
ios2->ios2_WireError = S2WERR_ALREADY_TRACKED;
}
stats = (struct SuperS2PTStats *)stats->ss_Node.mln_Succ;
}
if (!stats->ss_Node.mln_Succ) {
if(stats = AllocMem(sizeof(*stats), MEMF_CLEAR|MEMF_PUBLIC)) {
stats->ss_PType = type;
AddTail((struct List *)&adu->adu_Track, (struct Node *)stats);
}
}
UnlockUnit(adu);
TermIO(ios2);
}
/*
* This function removes a packet type from the
* list of those that are being tracked.
*/
static VOID
UnTrackType(struct AgnetDevUnit *adu, struct IOSana2Req *ios2)
{
struct SuperS2PTStats *stats;
LockUnit(adu);
stats = (struct SuperS2PTStats *)adu->adu_Track.mlh_Head;
while (stats->ss_Node.mln_Succ) {
if (ios2->ios2_PacketType == stats->ss_PType) {
Remove((struct Node *)stats);
FreeMem(stats, sizeof(*stats));
stats = NULL;
break;
}
stats = (struct SuperS2PTStats *)stats->ss_Node.mln_Succ;
}
if(stats) {
ios2->ios2_Req.io_Error = S2ERR_BAD_STATE;
ios2->ios2_WireError = S2WERR_NOT_TRACKED;
}
UnlockUnit(adu);
TermIO(ios2);
}
/*
* This function is called whenever a packet is received
*/
static VOID
PacketReceived(struct AgnetDevUnit *adu, ULONG length, ULONG type)
{
struct SuperS2PTStats *stats;
adu->adu_Stats.PacketsReceived++;
LockUnit(adu);
stats = (struct SuperS2PTStats *)adu->adu_Track.mlh_Head;
while (stats->ss_Node.mln_Succ) {
if (stats -> ss_PType == type) {
stats -> ss_Stats.PacketsReceived++;
stats -> ss_Stats.BytesReceived += length;
break;
}
stats = (struct SuperS2PTStats *)stats -> ss_Node.mln_Succ;
}
UnlockUnit(adu);
}
/*
* This function is called whenever a packet is sent
*/
static VOID
PacketSent(struct AgnetDevUnit *adu, ULONG length, ULONG type)
{
struct SuperS2PTStats *stats;
adu->adu_Stats.PacketsSent++;
LockUnit(adu);
stats = (struct SuperS2PTStats *)adu->adu_Track.mlh_Head;
while (stats->ss_Node.mln_Succ) {
if (stats -> ss_PType == type) {
stats -> ss_Stats.PacketsSent++;
stats -> ss_Stats.BytesSent += length;
break;
}
stats = (struct SuperS2PTStats *)stats -> ss_Node.mln_Succ;
}
UnlockUnit(adu);
}
/*
* This function is called a packet overruns
*/
static VOID
PacketOverrun(struct AgnetDevUnit *adu)
{
adu->adu_Stats.Overruns++;
DoEvent(adu, S2EVENT_HARDWARE | S2EVENT_RX | S2EVENT_ERROR);
}
/*
* This function is called whenever a packet with
* garbage data is encountered.
*/
static VOID
ReceivedGarbage(struct AgnetDevUnit *adu)
{
adu->adu_Stats.BadData++;
DoEvent(adu, S2EVENT_HARDWARE | S2EVENT_RX | S2EVENT_ERROR);
}
/*
* This function is called whenever a packet
* is dropped by the SLIP driver.
*/
static VOID
PacketDropped(struct AgnetDevUnit *adu, ULONG type)
{
struct SuperS2PTStats *stats;
LockUnit(adu);
stats = (struct SuperS2PTStats *)adu->adu_Track.mlh_Head;
while (stats->ss_Node.mln_Succ) {
if (stats -> ss_PType == type) {
stats -> ss_Stats.PacketsDropped++;
break;
}
stats = (struct SuperS2PTStats *)stats -> ss_Node.mln_Succ;
}
UnlockUnit(adu);
}
/*
* This function is called whenever an orphan packet
* is received
*/
static VOID
ReceivedOrphan(struct AgnetDevUnit *adu)
{
adu->adu_Stats.UnknownTypesReceived++;
}
/*
* This function handles S2_CONFIGINTERFACE commands.
*/
static VOID
ConfigInterface(struct AgnetDevUnit *adu, struct IOSana2Req *ios2)
{
/* Note: we may only be configured once. */
if (!(adu->adu_State & AGUF_CONFIG)) {
switch (adu->adu_HardwareType) {
case S2WireType_PPP:
case S2WireType_SLIP:
case S2WireType_CSLIP:
case S2WireType_Ethernet:
case S2WireType_IEEE802:
case S2WireType_Arcnet:
case S2WireType_LocalTalk:
case S2WireType_AmokNet:
/* Copy given address as our Station Address */
memcpy(&adu->adu_Addr, &ios2->ios2_SrcAddr, MAX_ADDR_BYTES);
/*FALLTHROUGH*/
default:
adu->adu_State |= AGUF_CONFIG;
}
} else {
/* Sorry, we're already configured. */
ios2->ios2_Req.io_Error = S2ERR_BAD_STATE;
ios2->ios2_WireError = S2WERR_IS_CONFIGURED;
}
TermIO(ios2);
}
/*
* This function handles S2_GETSTATIONADDRESS commands.
*/
static VOID
GetStationAddress(struct AgnetDevUnit *adu, struct IOSana2Req *ios2)
{
memcpy(ios2->ios2_DstAddr, adu->adu_Addr, MAX_ADDR_BYTES);
if (adu->adu_State & AGUF_CONFIG)
memcpy(ios2->ios2_SrcAddr, adu->adu_Addr, MAX_ADDR_BYTES);
else
memset(ios2->ios2_SrcAddr, 0xff, MAX_ADDR_BYTES);
TermIO(ios2);
}
/*
* This function handles S2_DEVICEQUERY comands.
*/
static VOID
DeviceQuery(struct AgnetDevUnit *adu, struct IOSana2Req *ios2)
{
struct Sana2DeviceQuery *sdq;
sdq = (struct Sana2DeviceQuery *)ios2->ios2_StatData;
sdq->DeviceLevel = 0L;
sdq->AddrFieldSize = adu->adu_AddrFieldSize;
sdq->MTU = adu->adu_MaxTU;
sdq->BPS = adu->adu_BPS;
sdq->HardwareType = adu->adu_HardwareType;
sdq->SizeSupplied = sizeof(*sdq);
TermIO(ios2);
}
/*
* This routime handles CMD_ONLINE commands.
*/
static VOID
Online(struct AgnetDevUnit *adu, struct IOSana2Req *ios2)
{
if (!(adu->adu_State & AGUF_ONLINE)) {
/* We're offline. Go online. */
LockUnit(adu);
if (DoOnline(adu)) {
/* Couldn't get online */
ios2->ios2_Req.io_Error = S2ERR_NO_RESOURCES;
ios2->ios2_WireError = S2WERR_GENERIC_ERROR;
}
UnlockUnit(adu);
}
TermIO(ios2);
}
/*
* Actual Online, return 0 on success
*
* Caller should have a lock on the unit
*/
LONG
DoOnline(struct AgnetDevUnit *adu)
{
struct AgnetDevice *adb = (struct AgnetDevice *)adu->adu_Device;
ULONG mtu; LONG i; BOOL error = TRUE;
struct DelayRequest *txed;
/* Allocate transfer buffer */
mtu = adu->adu_MaxTU + 64;
for (i = 0; i < MAX_TXED; i++) {
if (adu->adu_TxEd[i]) {
error = FALSE;
continue;
}
if (!(txed = CreateIORequest(&adb->ad_MsgPort,
sizeof(struct DelayRequest) + mtu)))
break;
error = FALSE;
adu->adu_TxEd[i] = txed;
txed->tr_node.io_Message.mn_Node.ln_Type = NT_REPLYMSG;
txed->tr_node.io_Device = adb->ad_Timer.tr_node.io_Device;
txed->tr_node.io_Unit = adb->ad_Timer.tr_node.io_Unit;
txed->tr_node.io_Command = TR_ADDREQUEST;
txed->dr_Unit = adu;
txed->dr_Len = mtu;
AddHead(&adu->adu_FreeToTx, txed);
}
if (!error) {
adu->adu_State |= AGUF_ONLINE;
GetSysTime(&adu->adu_Stats.LastStart);
/* In case someone wants to know...*/
DoEvent(adu, S2EVENT_ONLINE);
}
return error;
}
/*
* This routine handles CMD_OFFLINE commands.
* Any pending read or write request will be sent to
* their owners
*/
static VOID
Offline(struct AgnetDevUnit *adu, struct IOSana2Req *ios2)
{
struct AgnetDevice *adb = (struct AgnetDevice *)adu->adu_Device;
if (adu->adu_State & AGUF_ONLINE) {
/* We're online, so shut everything down. */
LockUnit(adu);
DoOffline(adu);
UnlockUnit(adu);
}
TermIO(ios2);
}
/*
* Put the unit offline
*
* Caller should have a lock on the unit
*/
VOID
DoOffline(struct AgnetDevUnit *adu)
{
struct AgnetDevice *adb = (struct AgnetDevice *)adu->adu_Device;
struct IOSana2Req *ios2;
int i;
struct DelayRequest *txed;
adu->adu_State &= ~AGUF_ONLINE;
while(ios2 = (struct IOSana2Req *)
RemHead((struct List *)&adu->adu_Rx)) {
ios2->ios2_Req.io_Error = S2ERR_OUTOFSERVICE;
ios2->ios2_WireError = S2WERR_UNIT_OFFLINE;
TermIO(ios2);
}
while(ios2 = (struct IOSana2Req *)
RemHead((struct List *)&adu->adu_RxOrph)) {
ios2->ios2_Req.io_Error = S2ERR_OUTOFSERVICE;
ios2->ios2_WireError = S2WERR_UNIT_OFFLINE;
TermIO(ios2);
}
while(ios2 = (struct IOSana2Req *)
RemHead((struct List *)&adu->adu_Tx)) {
ios2->ios2_Req.io_Error = S2ERR_OUTOFSERVICE;
ios2->ios2_WireError = S2WERR_UNIT_OFFLINE;
TermIO(ios2);
}
/*
* Abort still pending delayrequests,
* free all delayrequests
*/
for (i = MAX_TXED - 1; i >= 0; i--) {
if (txed = adu->adu_TxEd[i]) {
if (!CheckIO(txed))
AbortIO(txed);
WaitIO(txed); /* also removes from free queue */
DeleteIORequest(txed);
}
adu->adu_TxEd[i] = NULL;
}
DoEvent(adu, S2EVENT_OFFLINE);
}
/*
* This routine handles S2_ONEVENT commands
*/
static VOID
OnEvent(struct AgnetDevUnit *adu, struct IOSana2Req *ios2)
{
/* Two special cases.
* If we are (on/off)line, and ask (on/off)line event,
* we return immediately
*/
if (ios2->ios2_WireError == S2EVENT_ONLINE &&
adu->adu_State & AGUF_ONLINE ||
ios2->ios2_WireError == S2EVENT_OFFLINE &&
!(adu->adu_State & AGUF_ONLINE)) {
TermIO(ios2);
} else {
LockUnit(adu);
AddTail((struct List *)&adu->adu_Events, (struct Node *)ios2);
UnlockUnit(adu);
}
}
/*
* This routine is called whenever an "important"
* SANA-II event occurs.
*/
static VOID
DoEvent(struct AgnetDevUnit *adu, ULONG event)
{
struct IOSana2Req *ios2;
struct IOSana2Req *ios2_next;
LockUnit(adu);
ios2 = (struct IOSana2Req *)adu->adu_Events.mlh_Head;
while(ios2->ios2_Req.io_Message.mn_Node.ln_Succ) {
ios2_next = (struct IOSana2Req *)ios2->ios2_Req.io_Message.mn_Node.ln_Succ;
/* Is this the event they are looking for? */
if(ios2->ios2_WireError & event) {
Remove((struct Node *)ios2);
ios2->ios2_WireError = event;
TermIO(ios2);
}
ios2 = ios2_next;
}
UnlockUnit(adu);
}
/*
* This function is used for handling CMD_WRITE and S2_BROADCAST
* commands.
*/
static VOID
WritePacket(struct AgnetDevUnit *adu, struct IOSana2Req *ios2)
{
/* Make sure that we are online. */
if (!(adu->adu_State & AGUF_ONLINE)) {
/* Sorry, we're offline */
ios2->ios2_Req.io_Error = S2ERR_OUTOFSERVICE;
ios2->ios2_WireError = S2WERR_UNIT_OFFLINE;
TermIO(ios2);
return;
}
/* Is the data length legal? */
if (ios2->ios2_DataLength < adu->adu_MinTU ||
ios2->ios2_DataLength > adu->adu_MaxTU) {
/* Sorry, the packet is too long or too small! */
ios2->ios2_Req.io_Error = S2ERR_MTU_EXCEEDED;
ios2->ios2_WireError = S2WERR_GENERIC_ERROR;
TermIO(ios2);
DoEvent(adu, S2EVENT_SOFTWARE | S2EVENT_TX | S2EVENT_ERROR);
return;
}
/* We call the SendPacket if no delay */
ios2->ios2_Req.io_Flags &= ~IOF_QUICK;
{
struct DelayRequest *delayed;
LockUnit(adu);
AddTail((struct List *)&adu->adu_Tx,(struct Node *)ios2);
/*
* If there is free delay requests,
* try to send immediately
*/
delayed = (struct DelayRequest *)
RemHead((struct List *)&adu->adu_FreeToTx);
UnlockUnit(adu);
if (delayed)
SendPacket(delayed);
}
return;
}
/*
* This routine handles CMD_READ commands. We
* always queue these unless we're offline.
*/
static VOID
ReadPacket(struct AgnetDevUnit *adu, struct IOSana2Req *ios2)
{
if(adu->adu_State & AGUF_ONLINE) {
/* Queue it... */
LockUnit(adu);
AddTail((struct List *)&adu->adu_Rx, (struct Node *)ios2);
UnlockUnit(adu);
return;
}
/* Sorry, we're offline */
ios2->ios2_Req.io_Error = S2ERR_OUTOFSERVICE;
ios2->ios2_WireError = S2WERR_UNIT_OFFLINE;
TermIO(ios2);
}
/*
* This routine handles CMD_READORPHAN commands. We
* always queue these unless we're offline.
*/
static VOID
ReadOrphan(struct AgnetDevUnit *adu, struct IOSana2Req *ios2)
{
if(adu->adu_State & AGUF_ONLINE) {
/* Queue it... */
LockUnit(adu);
AddTail((struct List *)&adu->adu_RxOrph, (struct Node *)ios2);
UnlockUnit(adu);
return;
}
/* Sorry, we're offline */
ios2->ios2_Req.io_Error = S2ERR_OUTOFSERVICE;
ios2->ios2_WireError = S2WERR_UNIT_OFFLINE;
TermIO(ios2);
}
/*
* "Send" next queued packet
*/
static VOID
SendPacket(struct DelayRequest *delayed)
{
struct AgnetDevUnit *adu = delayed->dr_Unit;
struct BufferManagement *bm;
struct IOSana2Req *ios2 = NULL;
LONG length; ULONG delay;
LockUnit(adu);
while (!ios2) {
ios2 = (struct IOSana2Req *)RemHead((struct List *)&adu->adu_Tx);
/* There is nothing to send */
if (!ios2) {
AddHead((struct List *)&adu->adu_FreeToTx,(struct Node *)delayed);
UnlockUnit(adu);
return;
}
length = ios2->ios2_DataLength;
/* Update statistics */
PacketSent(adu, length, ios2->ios2_PacketType);
/* Should we lose the packet? */
if (adu->adu_Loss && adu->adu_Loss > LRandom() % LOSS_MAX) {
TermIO(ios2);
ios2 = NULL;
}
}
UnlockUnit(adu);
if (length > delayed->dr_Len)
length = delayed->dr_Len;
bm =(struct BufferManagement *) ios2->ios2_BufferManagement;
/* Copy the data out of the packet into timer request buffer. */
if (bm->bm_CopyFromBuffer &&
(*bm->bm_CopyFromBuffer) /* we should have this tag, really */
(delayed->dr_Data, ios2->ios2_Data, length)) {
delayed->dr_DataLen = length;
/* Copy addresses */
if ((delayed->dr_PPUnit = adu->adu_PPUnit) < 0) {
WORD adrlen = adu->adu_AddrFieldSize + 7 >> 3;
memcpy(delayed->dr_DstAddr, ios2->ios2_DstAddr, adrlen);
memcpy(delayed->dr_SrcAddr, adu->adu_Addr, adrlen);
}
/* Set hwtype */
delayed->dr_HardwareType = adu->adu_HardwareType;
delayed->dr_PacketType = ios2->ios2_PacketType;
/* We should recognize broadcasts or multicasts */
delayed->dr_Cmd = ios2->ios2_Req.io_Command;
/* Make errors */
if (adu->adu_Errors) {
LONG total = adu->adu_Errors * length * 8 / (LRandom() % ERRORS_MAX);
while (total-- > 0) {
/* Make a bit error.. */
ULONG position = LRandom() & 0x7fffffffL;
/* flip a bit */
delayed->dr_Data[(position / 8) % length] ^= 1 << (position % 8);
}
}
/* calculate delay */
delay = adu->adu_Delay;
if (delay && adu->adu_Deviation)
delay = RandomDev(delay, adu->adu_Deviation);
if (delay) {
delayed->tr_node.io_Command = TR_ADDREQUEST;
delayed->tr_time.tv_secs = delay / 1000;
delayed->tr_time.tv_micro = (delay % 1000) * 1000;
SendIO(delayed);
} else {
/* Fallthrough */
ReceivePacket(delayed);
}
} else {
/* Something went wrong...*/
ios2->ios2_Req.io_Error = S2ERR_NO_RESOURCES;
ios2->ios2_WireError = S2WERR_BUFF_ERROR;
DoEvent(adu, S2EVENT_SOFTWARE | S2EVENT_TX | S2EVENT_BUFF | S2EVENT_ERROR);
}
TermIO(ios2);
}
/*
* This routine is called whenever a packet is received.
* It return all appropriate read requests
*/
static VOID
ReceivePacket(struct DelayRequest *delayed)
{
struct AgnetDevUnit *adu;
int i;
if (delayed->dr_PPUnit >= 0 && delayed->dr_PPUnit < AD_MAXUNITS) {
adu = (struct AgnetDevUnit *)AgnetDeviceBase->ad_Units[delayed->dr_PPUnit];
if (adu)
DoReceive(adu, delayed);
} else {
for (i = 0; i < AD_MAXUNITS; i++) {
if (!(adu = (struct AgnetDevUnit *)AgnetDeviceBase->ad_Units[i]))
continue; /* No unit */
if (delayed->dr_HardwareType != adu->adu_HardwareType)
continue; /* Different hardware */
if (delayed->dr_Cmd != S2_BROADCAST &&
memcmp(delayed->dr_DstAddr, adu->adu_Addr,
adu->adu_AddrFieldSize + 7 >> 3))
continue; /* Address mismatched */
DoReceive(adu, delayed);
}
}
SendPacket(delayed);
}
/*
* DoReceive
*/
static VOID
DoReceive(struct AgnetDevUnit *adu, struct DelayRequest *delayed)
{
ULONG length = delayed->dr_DataLen;
ULONG type = delayed->dr_PacketType;
struct IOSana2Req *rxr;
/* Update statistics */
PacketReceived(adu, length, type);
if (adu->adu_MaxTU < length) {
PacketOverrun(adu);
return;
}
if (adu->adu_MinTU > length) {
ReceivedGarbage(adu);
return;
}
/* Find an appropriate request wanting this packet type */
LockUnit(adu);
for (rxr = (struct IOSana2Req *)adu->adu_Rx.mlh_Head;
rxr->ios2_Req.io_Message.mn_Node.ln_Succ;
rxr = (struct IOSana2Req *)rxr->ios2_Req.io_Message.mn_Node.ln_Succ) {
if (rxr->ios2_PacketType == type) {
Remove((struct Node *)rxr);
CopyBack(adu, rxr, delayed);
rxr = NULL;
break;
}
}
if (rxr) {
/* Nobody wants this packet type? So, it's orphan */
ReceivedOrphan(adu);
if (rxr = (struct IOSana2Req *)
RemHead((struct List *)&adu->adu_RxOrph)) {
rxr -> ios2_PacketType = type;
CopyBack(adu, rxr, delayed);
} else {
/* Nobody is interested in this packet, drop it */
PacketDropped(adu, type);
}
}
UnlockUnit(adu);
}
/*
* Copy received packet into a request,
* return the request to the caller
*/
static VOID
CopyBack(struct AgnetDevUnit *adu, struct IOSana2Req *ios2,
struct DelayRequest *delayed)
{
struct BufferManagement * bm =
(struct BufferManagement *)ios2->ios2_BufferManagement;
void *data = delayed->dr_Data;
ULONG length = delayed->dr_DataLen;
WORD adrlen = (adu->adu_AddrFieldSize + 7) >> 3;
/* Copy the data into the protocol stack's buffer using its
supplied callback routine. */
if (bm->bm_CopyToBuffer &&
(*bm->bm_CopyToBuffer)(ios2->ios2_Data, data, length)) {
ios2->ios2_DataLength = length;
if (delayed->dr_Cmd == S2_BROADCAST) {
ios2->ios2_Req.io_Flags |= SANA2IOB_BCAST;
memset(ios2->ios2_DstAddr, 0xff, adrlen);
} else {
memcpy(ios2->ios2_DstAddr, adu->adu_Addr, adrlen);
}
memcpy(ios2->ios2_SrcAddr, delayed->dr_SrcAddr, adrlen);
TermIO(ios2);
return;
} else {
ios2->ios2_DataLength = 0;
ios2->ios2_Req.io_Error = S2ERR_NO_RESOURCES;
ios2->ios2_WireError = S2WERR_BUFF_ERROR;
DoEvent(adu, S2EVENT_SOFTWARE | S2EVENT_RX | S2EVENT_BUFF | S2EVENT_ERROR);
TermIO(ios2);
return;
}
}